perm filename INUQ.MAC[IP,SYS] blob
sn#682167 filedate 1982-10-14 generic text, type T, neo UTF8
;CWL:<403-INET>INUQ.MAC.40303 26-Apr-82 15:48:39, Edit by CLYNN
; Fixed logical host
; Check INTON before perform a JSYS, INTQFK has owner's FORKX
;<TAPPAN.4>INUQ.MAC.14, 8-Mar-82 14:31:37, Edit by TAPPAN
; Fix bad max-packet-size value returned in ASNIQ%
;<TAPPAN.4>INUQ.MAC.13, 26-Feb-82 13:06:22, Edit by TAPPAN
; Add ASNIQ bit AQ%ICM (1B2) which allows the Q to
; send and receive ICMP messages
;<TAPPAN.4>INUQ.MAC.6, 23-Feb-82 18:25:07, Edit by TAPPAN
; Add INQICM (process user Q ICMP messages). Allow
; user to send an ICMP message if it if about the
; correct protocal/port/etc for his Q.
; flush logical host stuff since it was fairly bogus,
; and meaningful only with the arpanet.
; on SNDIN, if a user specifies the source host (in the packet)
; and it is one of our addresses, then leave it alone.
;<TAPPAN.4>INUQ.MAC.5, 22-Feb-82 18:44:00, Edit by TAPPAN
; merge
;<403-INET>INUQ.MAC.40301 29-Jan-82 15:14:13, Edit by CLYNN
; Updated for IP release 3
; Modularized protocols
; User interface definitions moved to MONSYM, Parameters to STG
;[BBNF]<402-INET>INUQ.MAC.51, 13-Jul-81 11:48:00, Ed: CLYNN
; Fix: Port swap in INQDS2
;[BBND]<402-INET>INUQ.MAC.50, 22-Jun-81 14:43:44, Ed: TAPPAN
; AT SNDIN0+24. CAILE T2,<MINIHS+3>/4 -> CAIGE
;[BBND]<402-INET>INUQ.MAC.48, 10-Feb-81 10:09:04, Ed: CLYNN
; Don't want address in T1, want error code
; SNDIN7 HRROI T1,[-1,,SNDIX1] HRROI T1,SNDIX1
; No packet to return when get to SNDIN8
; SNDIN8 SKIPA T1,[-1,,SNDIX2] HRROI T1,SNDIX2
; JRST SNDINX
;[BBND]SNARK:<402-INET>INUQ.MAC.47, 7-Nov-80 14:02:24
SEARCH INPAR,TCPPAR,PROLOG,MONSYM
IFN MNET,<SEARCH MNTPAR>
TTITLE INUQ
SUBTTL Internet User Queues, William W. Plummer, 27Feb79
SWAPCD
COMMENT !
These routines implement the user interface to the Internet world.
Once assigned, an Internet queue may be used to send and receive
messages to Internet hosts. The buffer in user space has only
a count word, an Internet header and Internet text. The gateway
selects an appropriate hardware interface, generates the required
local header for that network and sends the packet out.
* .ASNIQ ... 3 ...... Assign Internet queue JSYS
ASNIQ0 ... 4 ...... Guts of ASNIQ
* .RELIQ ... 7 ...... Release Internet queue JSYS
RELIQ0 ... 7 ...... Innards of RELIQ
REL1IQ ... 8 ...... Release packets from one Internet queue
* .SNDIN ... 9 ...... Send an Internet segment JSYS
SNDIN0 ... 9 ...... Send packet from user
* .RCVIN ... 12 ...... Receive an Internet segment JSYS
INQGET ... 14 ...... Get next segment off of a queue
CHKIQ ... 15 ...... Check user's access to queue
LHCHK ... 15 ...... Check non-zero logical host values
* INTDSP ... 16 ...... Dispatch incoming Internet segments
* INQPRC ... 17 ...... Dispatch packets to queues and find next run time
* INQDSP ... 18 ...... Dispatch segments to user queues
* INQCH0 ... 20 ...... Find next queue timeout time
* INQCHK ... 20 ...... Check if INQDSP is next protocol to run
* INQINI ... 21 ...... Internet queue initialization
* INTLGO ... 21 ...... Internet job logout routine
!
; Assign Internet Queue JSYS
;T1/ Flags,,pointer to QDB
; AQ%SCR==1B0 ; B0: Use secure interface.
; AQ%SPT==1B1 ; B1: Single(local) port protocol
; AQ%ICM==1B2 ; B2: Allow sending and receiving ICMP messages
; (Other flag bits must be 0)
;T2/ (Not currently used. Must be 0)
;T3/ (Not currently used. Must be 0)
; Note: Must have Net Wiz enabled
; ASNIQ
;Ret+1: Failed. Error code in T1. Owning job # in T2 if ASNSX2.
;Ret+2: OK. Internet queue handle in T1. Max buffer count in T2.
.ASNIQ::MCENT ; Enter monitor
SKIPN INTON ; IP initialized?
RETERR(ASNSX1) ; No, Cannot have queue
MOVX T4,SC%NWZ ; Capability bit for network wizard
TDNN T4,CAPENB ; Caller has it enabled?
RETERR(NTWZX1) ; No.
DMOVE T3,T1 ; Place for call via locked call
XMOVEI T1,INTQLK ; The lock to lock
XMOVEI T2,ASNIQ0 ; Function to call
CALL LCKCAL ; Call work routine while lock set
JUMPL T1,IQFAIL ; Finish up and give error return
UMOVEM T1,T1 ; Pass queue handle to caller
MOVE T2,INTXPW ; Biggest count word user can give
MOVEI T2,-PKTELI+1(T2) ; (without fragmentation)
UMOVEM T2,T2 ; Tell him this size
SMRETN ; Give success return
; Guts of ASNIQ JSYS
;T1/ From user
;T2/ From user
;INTQLK/locked NOINT
;
; CALL ASNIQ0
;Ret+1: Always. T1 has queue handle, or
; If error, T1 has -1,,errror (if T1 is ASNSX2, T2 has Job #)
ASNIQ0: STACKL <<USRQDB,.IQLEN>> ; Stack space for copy of user's QDB
LOCAL <QDB,IQ,FQ,FLGS>
MOVEM T1,QDB
MOVEM T1,FLGS ; Save flags
MOVEI T3,USRQDB ; Local copy
MOVE T2,T1 ; User data block
MOVEI T1,.IQLEN ; Length of a queue descriptor
CALL BLTUM ; Copy from user into monitor
MOVEI QDB,USRQDB ; Reference only our copy now
MOVSI T1,-.IQLEN ; Set to scan the QDB
HRR T1,QDB
MOVEI T2,17 ; Right four bits must be cleared
ANDCAM T2,0(T1)
AOBJN T1,.-1
MOVX T1,<-1B15> ; Mask for single port
TXNE FLGS,AQ%SPT ; Single port protocol?
ANDM T1,.IQPTM(QDB) ; Yes. Flush comparison on foreign port
;;; Following is useless
;;; SETCM T2,INETLB ; Real (non-logical) host bits
;;; MNET ...
;;; LSH T2,4 ; Left justify
;;; TDNE T2,.IQSHM(QDB) ; Cannot be called logical bits by user
;;; JRST ASNIQS ; Since conflicts with the system
MOVSI IQ,-NIQ ; Set to scan the queue tables
MOVEI FQ,0 ; Indicate no free slot found yet
ASNIQ1: MOVE T1,INTQJB(IQ) ; Get owner
CAME T1,[-1] ; Free?
JRST ASNIQ2 ; No. Go check for conflicts.
SKIPN FQ ; Already know of a free slot?
MOVE FQ,IQ ; No so save this one.
JRST ASNIQ8 ; And loop to next queue
; Check for conflicts with already assigned queues
ASNIQ2: MOVE T1,.IQPRM(QDB) ; Get protocol mask word from user's blk
AND T1,INTQM0(IQ) ; Compute least specific mask
MOVE T2,.IQPRV(QDB) ; Get value word
XOR T2,INTQV0(IQ) ; Compare against this queue
TDNE T1,T2 ; But only in the bits that matter
JRST ASNIQ8 ; Difference is OK. Try next.
MOVE T1,.IQFHM(QDB) ; Same for foreign host
AND T1,INTQM1(IQ)
MOVE T2,.IQFHV(QDB)
XOR T2,INTQV1(IQ)
TDNE T1,T2
JRST ASNIQ8
MOVE T1,.IQSHM(QDB) ; Same for local host
AND T1,INTQM2(IQ)
MOVE T2,.IQSHV(QDB)
XOR T2,INTQV2(IQ)
TDNE T1,T2
JRST ASNIQ8
MOVE T1,INTQJB(IQ) ; Get flags
XOR T1,FLGS ; Compare with request
TXNE T1,AQ%SPT ; Differ only in single/double port spec?
JRST ASNIQF ; Yes. Not allowed
MOVE T1,.IQPTM(QDB) ; Compare port word
AND T1,INTQM3(IQ)
MOVE T2,.IQPTV(QDB)
XOR T2,INTQV3(IQ)
TDNN T1,T2
JRST ASNIQF ; Give fail return due to conflict
; Doesn't conflict with this queue, try next
ASNIQ8: AOBJN IQ,ASNIQ1
JUMPE FQ,ASNIQN ; All slots full
; Assign queue to user
MOVE T1,.IQPRM(QDB) ; Copy into queue tables
MOVEM T1,INTQM0(FQ)
AND T1,.IQPRV(QDB)
MOVEM T1,INTQV0(FQ)
MOVE T2,.IQFHM(QDB)
MOVEM T2,INTQM1(FQ)
AND T2,.IQFHV(QDB)
MOVEM T2,INTQV1(FQ)
MOVE T3,.IQSHM(QDB)
MOVEM T3,INTQM2(FQ)
AND T3,.IQSHV(QDB)
MOVEM T3,INTQV2(FQ)
MOVE T4,.IQPTM(QDB)
MOVEM T4,INTQM3(FQ)
AND T4,.IQPTV(QDB)
MOVEM T4,INTQV3(FQ)
MOVE T1,JOBNO ; Our job number
HLL T1,FLGS ; Merge in the flags
MOVEM T1,INTQJB(FQ) ; Say we are the owner
CALL REL1IQ ; Flush old packets
SETZM INTQSP(FQ) ; No messages
MOVE T3,TODCLK ; When assigned
MOVEM T3,INTQTM(FQ)
MOVE T1,FORKX ; Fork ID of creator
HRROM T1,INTQFK(FQ) ; No fork waiting,,owner's FORKX
; fork for receive interrupt & support
HRRZ T1,FQ ; Get the queue handle for user
JRST ASNIQX ; Success return
;ASNIQS:TDZA T1,T1 ; Return Job 0 if reserved queue
ASNIQF: HRRZ T1,INTQJB(IQ) ; Get job number owning this queue
UMOVEM T1,T2 ; Give it to user
SKIPA T1,[ASNSX2] ; "Queue already in use"
ASNIQN: MOVEI T1,ASNSX1 ; "All queues in use"
HRROS T1 ; Make neg. left half to signal error
ASNIQX: RESTORE
RET
; Internet logout routine -- called by each job as it logs out
INTLGO::SETO T1, ; Say all Internet Queues
RELIQ ; Release them
JFCL
RET
; Release Internet Queue JSYS
;T1/ Internet Queue Handle or -1 for all owned by this job or
; (multiple) fork handle for all owned by requested fork(s).
; RELIQ
;Ret+1: Failure. Error code in T1
;Ret+2: Success
.RELIQ::MCENT ; Enter monitor
SKIPN INTON ; IP Initialized?
RETERR(SQX2) ; No, can't be assigned
MOVE T3,T1 ; Place arg for LCKCAL
AOSE T1 ; Check for -1
JUMP [HRRZ T1,T3 ; No, check RH only ??bug here??
CAIL T1,NIQ ; Is it a queue handle?
JRST RELIQ5 ; No, go check for fork handle
JRST .+1] ; Yes, back to normal code
XMOVEI T1,INTQLK ; The lock to lock
XMOVEI T2,RELIQ0 ; Function to call
CALL LCKCAL
JUMPL T1,IQFAIL
SMRETN
; Check for multiple fork handle argument
RELIQ5: CAIGE T1,.FHJOB ; Multiple handle?
JRST [CAIL T1,.FHSLF ; No, Job relative handle?
CAIL T1,.FHSLF+NLFKS
JRST RELIQ7 ; Neither, garbage
JRST .+1] ; Job relative handle, continue
IFKL < CALL FLOCK ; Lock fork structure
MOVX T2,<CALL RLIQFK> ; Call this routine
CALL MAPFKH ; Per fork
NOP ; Never blocks
CALL FUNLK ; Unlock fork structure
> ; End of IFKL
IFKA < CALL MAPFKH ; For each fork
CALL RLIQFK ; Call this routine
> ; End of IFKA
SMRETN ; All done
RELIQ7: HRRZ T1,SQX1 ; Bad IQ handle error
JRST MRETNE
; Lock INTQLK and do work per fork; LCKCAL of INTQLK needs to be here
; so MAPFKH won't ITRAP with INTQLK locked.
RLIQFK: MOVE T3,T1 ; Place FORKN for LCKCAL
XMOVEI T1,INTQLK ; The lock to lock
XMOVEI T2,RLIQF0 ; Function to call
CALL LCKCAL
RET
; Work routine for RLIQFK
; Called with T1 a FORKN & INTQLK locked
RLIQF0: LOCAL <IQ,FKX> ; Get FORKX corresponding
HRRZ FKX,SYSFK(T1) ; to MAPFKH's FORKN
MOVSI IQ,-NIQ ; Scan all queues
RLIQF1: MOVE T1,IQ ; Current handle
CALL CHKIQ ; Check access
JUMPL T1,RLIQFX ; Not this job
HRRZ T1,INTQFK(IQ) ; Get owning FORKX
CAME T1,FKX ; Owned by this fork?
JRST RLIQFX ; No, skip it
HRRZ T1,IQ ; Yes, get queue handle
CALL REL1IQ ; Release it and
SETOM INTQJB(IQ) ; Deassign it
SETZM INTQSP(IQ) ; Make sure no messages
MOVE T1,TODCLK ; Record when queue
MOVEM T1,INTQTM(IQ) ; was deassigned
RLIQFX: AOBJN IQ,RLIQF1 ; Loop through all queues
RESTORE
RET
; Innards of RELIQ
;T1/ IQ handle or -1 for all
;INTQLK/set NOINT
;
; CALL RELIQ0
;Ret+1: Always. T1 ge 0 if successful or -1,,errorcode if not
RELIQ0: LOCAL <IQ>
MOVEM T1,IQ
AOSE T1 ; Asked to do all for this job? (-1)
TLOA IQ,-1 ; No. Set AOBJN ptr to do just one
MOVSI IQ,-NIQ ; Yes. Set up for all
RELIQ2: MOVE T1,IQ ; Get the handle
CALL CHKIQ ; Check access.
JUMPL T1,RELIQ8 ; Jump if no access (different job)
HRRZ T1,IQ
CALL REL1IQ ; Flush packets in this queue
SETOM INTQJB(IQ) ; Deassign the queue
SETZM INTQSP(IQ) ; Make sure no messages
MOVE T1,TODCLK ; Record when queue
MOVEM T1,INTQTM(IQ) ; was deassigned
RELIQ8: AOBJN IQ,RELIQ2
RELIQ9: SETZ T1, ; Always successful
RESTORE
RET
; Routine to flush packets in queue if it is owned by calling job
;T1/ Internet Queue handle
;INTQLK/Set NOINT
;
; CALL REL1IQ
;Ret+1: Always.
REL1IQ: LOCAL <IQ>
MOVEM T1,IQ
HRRZ T2,INTQJB(IQ) ; Which job owns this one
CAME T2,JOBNO ; Us?
JRST REL1IX ; No.
REL1I1: HRRZ T1,IQ ; Get the handle
CALL INQGET ; Get a message if possible
JUMPL T1,REL1IX ; Jump if queue now empty.
CALL RETBLK ; Return the storage to free area
JRST REL1I1 ; Loop til queue empty
REL1IX: RESTORE
RET
; Send an Internet Segment JSYS
;T1/ Flags,,Internet Queue Handle (No flags defined. Must be 0)
;T2/ Buffer Address -> <words, inc this>,<IP header>,<IP data>
;T3/ Local net address for source route
;
; SNDIN
;Ret+1: Failed. Error code in T1.
;Ret+2: Success.
.SNDIN::MCENT ; Enter monitor
CALL SNDIN0 ; Do the work
JUMPL T1,IQFAIL
SMRETN
; Workhorse for SNDIN:
SNDIN0: LOCAL <IQ,BUF,SIZ>
NOINT
DMOVEM T1,IQ
XCTU [HRRZ SIZ,0(BUF)] ; Get size of user's buffer area
HRRZ T1,IQ ; The queue handle
CALL CHKIQ ; See if we have access to it
JUMPL T1,SNDINX ; Jump if not
; Not need this with fragmentation
HRROI T1,SNDIX1 ; Anticipate fail return
MOVE T2,INTXPW
SUBI T2,PKTELI-1
CAILE SIZ,<<MINIHS+3>/4> ; Must have size word & minimal IP header
CAMLE SIZ,T2 ; Must fit in our biggest packets
JRST SNDINX ; Give fail return
MOVEI T1,PKTELI-1(SIZ) ; Size of buffer needed here
CALL GETBLK ; Get a chunk of free storage
SKIPG PKT,T1 ; OK?
JRST SNDIN8
SETZM PKTFLG(PKT) ; Clear all internal flags
MOVEI T1,-1(SIZ) ; Number of words in user's area
MOVEI T2,1(BUF) ; First address in user's area
XMOVEI T3,PKTELI(PKT) ; First address in monitor area
CALL BLTUM ; Move it into the monitor
LOAD T1,PIPL,(PKT) ; Packet length in bytes
ADDI T1,3 ; Set to round up
ASH T1,-2 ; Number of words which must be present
CAIL T1,0(SIZ) ; Must be in what we were given
JRST SNDIN9 ; No. Length error
LOAD T2,PIDO,(PKT) ; Get data offset in 32-bit words
CAIGE T2,<MINIHS+3>/4 ; Must be at least a full header
JRST SNDIN9
SKIPE INTQM3(IQ) ; Filtering on ports?
CAIGE T2,0(T1) ; Yes. Pkt must include ports.
CAILE T2,0(T1) ; Pkt must always include min header
JRST SNDIN9
LOAD T1,PIVER,(PKT) ; Pick up the Internet version
CAIE T1,.INTVR ; Is that right?
JRST SNDIN9
LOAD T1,PIPRO,(PKT) ; Protocol
CAIN T1,.ICMFM ; ICMP?
JRST SNDINC ; Yes, treat specially
LSH T1,↑D4 ; 'left justify' for TSTIQ
LOAD T4,PIDO,(PKT) ; Get Internet data offset
ADD T4,PKT ; Get address of port word
MOVE T4,PKTELI(T4) ; Get the port word
JRST SNDIN3
;;; Here if the user is sending an ICMP packet, it must be for
;;; the appropriate protocal/host/port
;;; We check the protocal and ports in the packets "error header"
;;; rather than those in the main header
SNDINC: MOVE T2,INTQJB(IQ) ; Get flags
TXNN T2,AQ%ICM ; ICMP messages enabled?
JRST SNDIN7 ; Return an error
LOAD T2,PIDO,(PKT) ; Get data offset
ADD T2,PKT ; Add it in
ADDI T2,.CMINH ; Plus header offset in packet
LOAD T1,PIPRO,(T2) ; get the protocal from the header
LSH T1,4 ; Shift for TSTIQ
LOAD T4,PIDO,(T2) ; get length from header
ADD T4,T2 ; Add in
MOVE T4,PKTELI(T4) ; Get port word
SNDIN3:
MOVE T2,PKTELI+.IPKSH(PKT) ; Get source
MOVE T3,PKTELI+.IPKDH(PKT) ; and dest
CALL TSTIQ ; All good for this Q?
JRST SNDIN9 ; No, error.
LOAD T1,PISH,(PKT) ; Get packet source
JUMPE T1,SNDIN4 ; User did not specifiy
IFN MNET,<CALL LCLHST> ; Is it one of us?
IFE MNET,<CAME T1,INETID> ; Is it us?
JRST SNDIN9 ; Bad address
JRST SNDIN5 ; Skip
SNDIN4: MOVE T1,INETID ; Default source for packet
STOR T1,PISH,(PKT) ; Save
SNDIN5:
MOVE T1,INTQJB(IQ) ; Get flags
TXNN T1,AQ%SCR ; RPI desired?
JRST SNDIN6 ; No
SETONE PSCR,(PKT) ; Flag the packet for that interface
SNDIN6:
XCTU [SKIPN T3] ; Source route address in T3?
JRST SNDINS ; No, go send
SETONE PSROU,(PKT) ; Flag to do source routing
SNDINS:
CALL SNDGAT ; Send it. Low lvl code will return stg
SETZ T1, ; Tell caller all is ok
JRST SNDINX
; Error returns
SNDIN7: HRROI T1,SNDIX1 ; Header or format problem: SIZ, PIPL,
JRST SNDINE ; PIDO, ports, PIVER, PIPRO, S, D
SNDIN8: HRROI T1,SNDIX2 ; No storage error
JRST SNDINX ; Don't return what we didn't get
SNDIN9: HRROI T1,SNDIX4
SNDINE: PUSH P,T1
CALL RETPKT ; Return storage used for packet
POP P,T1
SNDINX: OKINT
RESTORE
RET
; Receive an Internet Segment JSYS
;T1/ Flags,,Internet Queue Handle
; RIQ%NW On to give error return instead of waiting
;T2/ Buffer pointer -> <act,,max, inc this>,<IP header>,<IP data>
;T3/ (Not currently used. Must be 0)
;
; RCVIN
;Ret+1: Failed. Code in T1.
;Ret+2: Success.
.RCVIN::MCENT ; Enter the monitor
CALL RCVIN0 ; Do work
CAMN T1,[-1] ; Nothing waiting error (RIQ%NW was 1)
JRST MRETNE ; Return -1
JUMPL T1,IQFAIL ; Return 0,,errorcode
SMRETN ; No error return
; Guts of RCVIN
; CALL RCVIN0
;Ret+1: Always, T1 is: -1, .lt. 0, or .ge. 0
RCVIN0: LOCAL <IQ,BUF>
RCVIN1: NOINT
UMOVE IQ,T1 ; Get the queue handle & flags
UMOVE BUF,T2 ; And user's buffer address
HRRZ T1,IQ ; Get the handle
CALL CHKIQ ; See if we have access to it
JUMPL T1,RCVINX ; Jump if not (T1 has error code)
HRRZ T1,IQ
CALL INQGET ; Try to get a segment from that queue
JUMPGE T1,RCVIN2 ; Jump if we did
TXNE IQ,RIQ%NW ; Check the "don't wait" flag
JRST RCVINX ; Don't wait, return -1 (from INQGET)
MOVE T1,FORKX ; Must wait. Get our fork number
HRLM T1,INTQFK(IQ) ; Leave it for when segment arrives
OKINT
XMOVEI T1,INTQFK(IQ) ; The cell to wait on
CALL DISL ; Wait for it to be gronked
JRST RCVIN1 ; Go try again
; Still NOINT
RCVIN2: MOVE PKT,T1 ; Put packet pointer in proper place
LOAD T1,PIPL,(PKT) ; Get the Internet pkt length in bytes
ADDI T1,4+3 ; Allow for bfr length word. Round up.
ASH T1,-2 ; Number of words required.
XCTU [HRRZ T2,0(BUF)] ; Get user's buffer size
XCTU [HRLM T1,0(BUF)] ; Tell him what's needed/present
SETZ IQ, ; Assume no truncation error
CAMG T1,T2 ; Enough space available?
JRST RCVIN3 ; Yes
MOVE T1,T2 ; No. Give him what we can.
HRROI IQ,SNDIX1 ; Remember to give error return
RCVIN3:
SOSG T1 ; Don't count the count word
JRST RCVIN4 ; User area too small
XMOVEI T2,PKTELI(PKT) ; First word in Internet part
MOVEI T3,1(BUF) ; First word in user's data area
CALL BLTMU ; Transfer it to the user
RCVIN4:
OKINT
CALL RETPKT ; Return the packet storage
SKIPA T1,IQ ; Error code
RCVINX: OKINT ; For fatal error returns
RESTORE
RET ; No.
; Get a message (Internet segment) from specified queue
;T1/ Internet Queue Handle
;INTQLK/ Locked NOINT
;
; CALL INQGET
;Ret+1: Always. T1 has pointer to message if any, or is -1 if none
INQGET: LOCAL <IQ>
MOVEM T1,IQ
NOSKED ; Prevent simultaneous access to Q
ADD T1,INTQHD ; Compute address of queue head
MOVE T2,T1
LOAD T1,QNEXT,(T2) ; Get first thing on queue
CAIN T1,0(T2) ; If that is the queue head itself,
JRST INQGE9 ; The queue is empty
SETSEC T1,INTSEC ; Make extended address
CALL DQ ; Dequeue the message and return in T1
SOSA INTQSP(IQ) ; Credit space to queue
; Success return. T1 has the message
INQGE9: SETO T1, ; Fail return.
OKSKED
MOVE T2,TODCLK ; "Now"
SKIPLE INTQSP(IQ) ; Messages remaining?
ADD T2,INTQT0 ; Yes, Bump no-activity time out
; SKIPL T1
MOVEM T2,INTQTM(IQ) ; Otherwise record when examined
RESTORE
RET
; Check that the calling job has access an Internet Queue
;T1/ Internet Queue Handle
; NOINT
; CALL CHKIQ
;Ret+1: Always. T1 ge 0 if ok, -1,,error code if not
CHKIQ: HRRZS T2,T1
CAIL T2,NIQ ; Range check the handle
JRST CHKIQ9 ; Bad
HRRZ T2,INTQJB(T2) ; Get JOBNO which owns this queue
CAMN T2,JOBNO ; Is that this job?
RET ; Yes. T1 still has the Q index
SKIPA T1,[-1,,SQX2] ; Owned by some other job
CHKIQ9: HRROI T1,SQX1 ; Bad handle
RET
; Common exit when T1 has -1,,error code
IQFAIL: HRRZS T1
JRST MRETNE
; LHCHK - Check non-zero logical host values
; T1/ Masked logical host value
; T2/ Protocol (preserved of bashed if for user queue)
; P1/ Interface (if MNET)
; CALL LHCHK
; Return+1 Always, T2 changed if destined for user queue
LHCHK: MOVE T3,T1 ; Masked value (non-zero)
IFN MNET,<MOVE T4,NTNLHM(P1)> ; Mask
IFE MNET,<MOVE T4,INETLB>
; Know logical host must be in rightmost 24 bits
; TDNN T4,[037777,,777777]
; LSHC T3,-40 ; Right justify mask & value
TRNN T4,177777
LSHC T3,-20
TRNN T4,377
LSHC T3,-10
TRNN T4,17
LSHC T3,-4
TRNN T4,3
LSHC T3,-2
TRNN T4,1
LSHC T3,-1
CAMG T3,INTLHX ; Deliver to protocol or user queue?
RET ; Protocol
LSH T3,10
IOR T2,T3 ; Logical Host,Protocol
RET ; User - Make protocol compares fail
; Dispatch messages from the gateway to the right Internet Queue
; (No args)
;
; CALL INTDSP
;Ret+1: Always. Internet Input Queue empty.
INTDSP::
IFN MNET,<SAVP1>
LOCAL <PIX,PTB,PTL>
PUSH P,PKT
INTDS0: CALL RCVGAT ; Get a message from the gateway
JUMPE PKT,INTDSX ; None available. We're done for now.
XMOVEI PTB,INTPIX+1 ; Locate tables
MOVE PIX,-1(PTB) ; # Protocols
HRRZ PTL,PIX ; Table length
LOAD T2,PIPRO,(PKT) ; Internet protocol
;;; Kludge for handling CHAOS packets
IFDEF CHAINT,<
CAIE T2,.CHAFM ; Chaos protocal?
JRST INTDS2 ; No, join below
CALL CHIINT ; give packet to protocal
CALL RETPKT ; Give back storage
JRST INTDS0 ; and look for more
>
LOAD T1,PIDH,(PKT) ; Find interface for the address
CALL NETNCT ; by which we were addressed
TDZA T1,T1 ; None ???
AND T1,NTNLHM(P1) ; Logical host mask for dest's net
SKIPE T1 ; Logical host 0 uses systemprotocols
CALL LHCHK ; Check non-zero logical host values
INTDS2: SKIPN ,INTPO(PTB) ; Protocol on?
JRST INTDS3 ; No
SKIPL T3,.INTPL(PTB) ; Take any protocol
CAMN T2,T3 ; or match
JRST INTDS4 ; Yes
INTDS3:
ADD PTB,PTL ; Not for this protocol module
AOBJN PIX,INTDS2 ; Try next
; Shouldn't get here because user queues should take anything
MOVX T1,PT%KIP ; Invalid protocol
TDNE T1,INTTRC ; Want trace?
CALL PRNPKI ; Yes
MOVX T1,<DU%PRO,,ICM%DU> ; Protocol unreachable
CALL ICMERR ; (releases storage)
JRST INTDS0 ; and process next
; Found protocol
INTDS4: MOVE T1,PKT ; What to enqueue
MOVE T2,.INTPQ(PTB) ; Where to enqueue it
NOSKED ; In case protocol run by different fork
CALL NQ
OKSKED
AOS .INTPF(PTB) ; Say a pkt waiting
;why? AOS INTFLG ; And keep this fork running
JFCL
MOVX T1,PT%IQP ; Packet given to protocol
TDNE T1,INTTRC ; Want trace?
CALL PRNPKI ; Yes
JRST INTDS0 ; Process another message
INTDSX: POP P,PKT
RESTORE
RET
; Internet User Queue Process:
; Dispatch Internet messages to a user-assigned queue
; Find next run time (when a queue timesout)
;
; CALL INQPRC
;Ret+1: Always. Packets queued to user or flushed; INQTIM set
INQPRC::PUSH P,PKT ; For subroutines
SETZM INQFLG ; No rerun required
SKIPN INQON ; Been initialized?
JRST INQPRX ; No??
; Pass out all packets
INQPR0: MOVE T2,INQIPQ ; Locate Queue head
NOSKED
LOAD T1,QNEXT,(T2) ; Get first thing
CAIN T1,(T2) ; Empty?
JRST INQPR2 ; Yes
SETSEC T1,INTSEC
CALL DQ
OKSKED
MOVE T3,T1 ; Arg for that routine is packet
XMOVEI T1,INTQLK ; Internet queue lock
XMOVEI T2,INQDSP ; Routine to call
CALL LCKCAL ; Call routine while lock set
JRST INQPR0 ; Get rest
INQPR2: OKSKED
XMOVEI T1,INTQLK ; Lock to lock
XMOVEI T2,INQCH0 ; Function to call
CALL LCKCAL
MOVEM T1,INQTIM ; Next timeout
INQPRX: POP P,PKT
RET
;T1/ Pointer to packet
;INTQLK/set NOINT
;
; CALL INQDSP ; do work for INQPRC
;Ret+1: Always. Packet queued or flushed.
INQDSP: ACVAR <IQ,IQERR>
MOVE PKT,T1
MOVX IQERR,<DU%PRO,,ICM%DU> ; Assume no protocol
MOVSI IQ,-NIQ ; Set to scan them
INQDS2: MOVE T1,INTQJB(IQ) ; Get owner of queue
CAMN T1,[-1] ; Assigned?
JRST INQDS5 ; No. Go try next.
TXNN T1,AQ%SCR ; Secure queue?
TDZA T1,T1 ; No
MOVX T1,1 ; Yes
LOAD T2,PSCR,(PKT) ; Get interface class (secure or not)
CAME T1,T2 ; Packet class matches queue?
JRST INQDS5 ; No. Try next.
LOAD T1,PIPRO,(PKT) ; Get Internet Protocol number
LSH T1,↑D4
XOR T1,INTQV0(IQ) ; Compare
TDNE T1,INTQM0(IQ) ; But only in bits that matter
JRST INQDS5 ; Not for this queue
MOVE T2,PKTELI+.IPKSH(PKT) ; Source address
XOR T2,INTQV1(IQ)
TDNE T2,INTQM1(IQ)
JRST INQDS5
LOAD T1,PIDH,(PKT) ; Destination address
LSH T1,↑D4 ; Position 32-bit wise
XOR T1,INTQV2(IQ) ; Compare with source logical host
TDNE T1,INTQM2(IQ) ; In the bits which matter
JRST INQDS5
MOVE T1,INTQJB(IQ) ; Get flags
LOAD T3,PIPL,(PKT) ; Get packet length in bytes
LOAD T4,PIDO,(PKT) ; Get IN hdr length in words
ASH T4,2 ; Convert to bytes
SKIPN INTQM3(IQ) ; Filtering on ports?
JRST INQDS3 ; No, have length required
ADDI T4,4 ; 2 Ports take 4 more bytes
TXNE T1,AQ%SPT ; But a single port only
SUBI T4,2 ; Take 2 bytes
INQDS3: CAMGE T3,T4 ; Enough in packet?
JRST INQDS5 ; Maybe more luck on a different queue
SKIPN INTQM3(IQ) ; Should we do the port compare?
JRST INQDS6 ; No. We found the right queue
MOVX IQERR,<DU%PRT,,ICM%DU> ; Port Unreachable
LOAD T4,PIDO,(PKT) ; Get Internet Data Offset
ADD T4,PKT ; Add base of packet
MOVE T3,PKTELI(T4) ; Get FP.LP and 4 extra bits
LDB T4,[POINT 16,T3,15] ; Save foreign port
TXNN T1,AQ%SPT ; Single port protocol? FP.FP
LSH T3,↑D16 ; No. Move Local port LP.FP
DPB T4,[POINT 16,T3,31] ; Plop in the foreign port
XOR T3,INTQV3(IQ)
TDNE T3,INTQM3(IQ)
INQDS5: AOBJN IQ,INQDS2
JUMPGE IQ,INQDS9 ; Flush it if no queue found
INQDS6: MOVE T1,INTQSP(IQ) ; Number of messages on this queue
CAML T1,INTQMX ; Less than number allowed?
JRST INQDS8 ; No. Flush this one.
AOS INTQSP(IQ) ; Count space
MOVE T3,TODCLK ; "Now"
ADD T3,INTQT0 ; Deadman timeout
HRRZ T2,IQ ; Queue index
ADD T2,INTQHD ; Get pointer to the queue head
LOAD T1,QNEXT,(T2) ; Get first thing on queue
CAIN T1,0(T2) ; If the head itself, queue is empty.
MOVEM T3,INTQTM(IQ) ; Keep away the grim reaper
MOVE T1,PKT ; What to enqueue. T2 has where
NOSKED
CALL NQ
OKSKED
PUSH P,7 ; Protect critical AC
SKIPGE 7,INTQFK(IQ) ; See if a fork is waiting on this queue
JRST INQDS7 ; No
HRROS INTQFK(IQ) ; Forget that and make its wait complete
IFKA < CALL PRWAKE> ; Get scheduler to run him now (NOINT)
INQDS7: POP P,7
JRST INQDSX ; Try for another segment
; Errors
INQDS8: MOVX IQERR,ICM%SQ ; Source Quench
INQDS9: MOVX T1,PT%UKQ
CAIN IQERR,ICM%SQ ; Was error Source Quench?
MOVX T1,PT%UKS ; Yes
TDNE T1,INTTRC ; Want trace?
CALL PRNPKI ; Yes
MOVE T1,IQERR ; Report error
CALL ICMERR ; (Releases storage)
INQDSX: RET
PURGE IQ,IQERR
;;; Give an ICMP message to a user Q
;;; PKT/ message
INQICM::XMOVEI T1,INTQLK ; Point to lock
XMOVEI T2,INQIC0 ; Routine to call
CALL LCKCAL ; Lock and call the routine
RETSKP ; (packet taken or flushed)
;;; Workhorse routine
INQIC0: ACVAR <IQ,IQERR,CPKT> ; IQ and IQERR must match that at INQDSP
LOAD CPKT,PIDO,(PKT) ; Get packet data offset
ADD CPKT,PKT ; Add in to get to ICMP packet
ADDI CPKT,.CMINH ; And point to the INET header therein
MOVSI IQ,-NIQ ; Number of user Q's
INQIC1: MOVE T2,INTQJB(IQ)
TXNE T2,AQ%ICM ; ICMP messages allowed on this Q?
CAMN T2,[-1] ; Is this Q in use?
JRST INQIC5 ; No
;;; Should we check for secure here???
LOAD T1,PIPRO,(CPKT) ; Get protocal
LSH T1,4 ; Shift
MOVE T2,PKTELI+.IPKSH(CPKT) ; Source host
MOVE T3,PKTELI+.IPKDH(CPKT) ; Destination host
LOAD T4,PIDO,(CPKT) ; Get data offset
ADD T4,CPKT ; add in
MOVE T4,PKTELI(T4) ; and get port word
CALL TSTIQ ; Match this Q?
CAIA ; No
CALLRET INQDS6 ; Join above to put packet on Q
INQIC5: AOBJN IQ,INQIC1 ; Loop through
CALL RETPKT ; Nothing matches, flush
RET ; and return
;;; TSTIQ -- Check that parameters match a special Q
;;; Called
;;; T1/ Protocal
;;; T2/ Source host
;;; T3/ destination host
;;; T4/ port word
;;; (all left justified)
;;; (This is only valid for testing packets that we are sending
;;; or have sent), expects IQ to be in Q1
;;; Returns +2 if the paremeters match the given Q
TSTIQ: XOR T1,INTQV0(IQ) ; Check
TDNE T1,INTQM0(IQ) ; Match?
RET ; no
XOR T2,INTQV2(IQ) ; source
TDNE T2,INTQM2(IQ) ; ?
RET
XOR T3,INTQV1(IQ) ; dest
TDNE T3,INTQM1(IQ) ; ?
RET
XOR T4,INTQV3(IQ) ; Ports
TDNE T4,INTQM3(IQ) ; ?
RET
RETSKP ; match
PURGE IQ,IQERR,CPKT
;;; ENDAV.
; Find next timeout over all queues, and flush timedout packets
;INQLCK/ Locked NOINT
;
; CALL INQCH0
;Ret+1: Always. Minimum time in T1
INQCH0: LOCAL <IQ,TOD,NXT>
MOVX NXT,<377777777777>
MOVE TOD,TODCLK ; "Now"
MOVSI IQ,-NIQ ; Set to scan all queues
INQCH1: MOVE T1,INTQJB(IQ) ; Get owner
CAME T1,[-1] ; Is this queue assigned?
SKIPG INTQSP(IQ) ; And have received packets?
JRST INQCH9 ; No. Try next.
CAMG TOD,INTQTM(IQ) ; Has user forgotten it?
JRST INQCH8 ; No
; Forgotten queue gets flushed
INQCH2: HRRZ T1,IQ ; Get the queue handle
CALL INQGET ; Get a message from that queue
JUMPL T1,INQCH7 ; Jump if none left
MOVE PKT,T1 ; For trace
MOVX T1,PT%UKT ; Delivery time exceeded
TDNE T1,INTTRC ; Want trace?
CALL PRNPKI ; Yes
MOVX T1,<TE%TTL,ICM%TE> ; Report Error
CALL ICMERR ; (releases storage)
JRST INQCH2 ; Loop over the whole queue
INQCH7: SETZM INTQSP(IQ) ; Better be zero if all flushed
MOVE T1,TODCLK
MOVEM T1,INTQTM(IQ) ; Remember when flushed
JRST INQCH9
INQCH8: CAMLE NXT,INTQTM(IQ) ; Min next check against timeout
MOVE NXT,INTQTM(IQ)
INQCH9: AOBJN IQ,INQCH1 ; Loop over all queues
MOVE T1,NXT ; Value to return
RESTORE
RET
; See if user queues are next thing to go
;T1/ TODCLK of when next check is needed by checkers run so far
;
; CALL INQCHK
;Ret+1: Always. T1 has TODCLK when to check next.
INQCHK::SKIPN INQON
RET
CAMLE T1,INQTIM ; Clock to check
MOVE T1,INQTIM ; Next time something timesout
RET
; Internet Queue Initialization:
INQINI::LOCAL <IQ>
MOVX T1,<377777777777>
MOVEM T1,INQTIM ; Don't need to be run
SETZM INQFLG
SETOM INQPCL ; Protocol -1 to accept anything
MOVX T1,QSZ ; Get Input queue head
CALL GETBLK
JUMPLE T1,INQIN0 ; Lose
CALL INITQ ; Initialize it
MOVEM T1,INQIPQ ; Set pointer to it
MOVEI T1,NIQ ; Number of queues
CALL GETBLK ; Get a block of free storage for heads
JUMPLE T1,INQIN0 ; No space
NOSKED
MOVEM T1,INTQHD ; Save pointer to the area
MOVSI IQ,-NIQ ; Set to scan the queue heads
INQIN1: HRRZ T1,IQ ; Current index
ADD T1,INTQHD ; Plus base is the queue head
CALL INITQ ; Initialize that queue
SETOM INTQJB(IQ) ; Say queue not owned
SETZM INTQTM(IQ)
AOBJN IQ,INQIN1 ; Loop through all
XMOVEI T1,INTQLK ; Pointer to the lock
CALL CLRLCK ; Initialize it
OKSKED
SETOM INQON ; All set
CAIA
INQIN0: INBUG(HLT,<INQINI: Freestorage gone?>,INGWA1)
RESTORE
RET
TNXEND
END